﻿using SharpDepend.WinRT.Classes;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.Foundation;
using Windows.Media.Capture;
using Windows.Storage;
using Windows.Storage.Streams;
using Windows.UI.Xaml.Media.Imaging;
using System.Runtime.InteropServices.WindowsRuntime;
using System.IO;
using Windows.ApplicationModel.Core;
using SharpDepend.Datatypes;
using Windows.Graphics.Imaging;

namespace SharpDepend.WinRT
{
    class CameraManager : ICameraManager
    {
        class Camera : ICamera
        {
            private CameraCaptureUI mCamera;
            //private WriteableBitmap wBitmap;
            private BitmapImage bitmapCamera;

            private const int BUFFER = 1024;
            private byte[] bufferData = new byte[BUFFER];
            
            private int[] mPreviewData = new int[0];
            private int mPreviewDataLength;

            public Camera(string name)
            {
                Name = name;

                mCamera = new CameraCaptureUI();
            }

            public string Name { get; private set; }

            public int PixelHeight { get; private set; }

            public int PixelWidth { get; private set; }

            public bool Ready { get; private set; }

            public void GetPreviewBuffer(int[] data)
            {
                //int i = -1;
                //for (int x = 0; x < PixelWidth; x++)
                //{
                //    for (int y = 0; y < PixelHeight; y++)
                //    {
                //        int index = PixelHeight * y + x;
                //
                //        data[++i] = mPreviewData[index];
                //    }
                //}
                Array.Copy(mPreviewData, data, mPreviewDataLength);
            }

            public void StartPreview()
            {
                if (bitmapCamera != null)
                {
                    throw new InvalidOperationException("Camera can only start once. It must be stopped first.");
                }
                
                IAsyncOperation<StorageFile> capturedMediaHandle = mCamera.CaptureFileAsync(CameraCaptureUIMode.Photo);

                // Note: WinRT do not support Helper.WaitToComplete(capturedMediaHandle);
                //       It is because WinRT has an own CameraUI for taking photos so no
                //       preview can be fetch. So when you have take a photo with WinRT camera ui
                //       this "Completed" code will be called and the "Preview" panel in XamarinForms
                //       will be the picture you have token.
                capturedMediaHandle.Completed = delegate
                {
                    var capturedMedia = capturedMediaHandle.GetResults();

                    if (capturedMedia != null)
                    {
                        IAsyncOperation<IRandomAccessStream> streamCameraHandle = capturedMedia.OpenAsync(FileAccessMode.Read);

                        Helper.WaitToComplete(streamCameraHandle);

                        using (var streamCamera = streamCameraHandle.GetResults())
                        {
                            var mainThreadHandle = CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.High,
                            () =>
                            {
                                // TODO: BitmapImage can only be created in MainThread.
                                bitmapCamera = new BitmapImage();
                                bitmapCamera.SetSource(streamCamera);
                                
                                PixelWidth = bitmapCamera.PixelWidth;
                                PixelHeight = bitmapCamera.PixelHeight;

                                //wBitmap = new WriteableBitmap(PixelWidth, PixelHeight);

                                IAsyncOperation<IRandomAccessStream> streamHandle = capturedMedia.OpenAsync(FileAccessMode.Read);

                                Helper.WaitToComplete(streamHandle);

                                using (var stream2 = streamHandle.GetResults())
                                {
                                    //wBitmap.SetSource(stream2);

                                    mPreviewDataLength = PixelWidth * PixelHeight;
                                    if (mPreviewDataLength > mPreviewData.Length)
                                    {
                                        mPreviewData = new int[mPreviewDataLength];
                                    }

                                    int readed = BUFFER;
                                    int current = 0;

                                    // Reference: https://msdn.microsoft.com/sv-se/library/windows/apps/br243259
                                    
                                    var decoderHandle = BitmapDecoder.CreateAsync(stream2);
                                    Helper.WaitToComplete(decoderHandle);
                                    BitmapDecoder decoder = decoderHandle.GetResults();

                                    BitmapTransform transform = new BitmapTransform()
                                    {
                                        ScaledWidth = Convert.ToUInt32(PixelWidth),
                                        ScaledHeight = Convert.ToUInt32(PixelHeight)
                                    };

                                    var pixelDataHandle = decoder.GetPixelDataAsync(
            BitmapPixelFormat.Rgba8, // WriteableBitmap uses BGRA format 
            BitmapAlphaMode.Straight,
            transform,
            ExifOrientationMode.IgnoreExifOrientation, // This sample ignores Exif orientation 
            ColorManagementMode.DoNotColorManage
        );
                                    Helper.WaitToComplete(pixelDataHandle);
                                    PixelDataProvider pixelData = pixelDataHandle.GetResults();

                                    // An array containing the decoded image data, which could be modified before being displayed 
                                    byte[] sourcePixels = pixelData.DetachPixelData();

                                    System.Buffer.BlockCopy(sourcePixels, 0, mPreviewData, 0, sourcePixels.Length);
                                    
                                    //using (var stream = wBitmap.PixelBuffer.AsStream())
                                    //{
                                    //    while (readed == BUFFER)
                                    //    {
                                    //        readed = stream.Read(bufferData, 0, BUFFER);
                                    //
                                    //        System.Buffer.BlockCopy(bufferData, 0, mPreviewData, current, readed);
                                    //
                                    //        current += readed;
                                    //    }
                                    //}

                                }
                            });

                            Helper.WaitToComplete(mainThreadHandle);
                        }
                    }

                    Ready = true;
                };
            }

            public void StopPreview()
            {
                if (bitmapCamera == null)
                {
                    throw new InvalidOperationException("Camera is already stopped.");
                }

                Ready = false;

                bitmapCamera = null;
            }
        }

        private List<ICamera> mAvailableCameras;

        public CameraManager()
        {
            mAvailableCameras = new List<ICamera>();
            mAvailableCameras.Add(new Camera("Camera UI"));
        }

        public IEnumerable<ICamera> AvailableCameras
        {
            get
            {
                return mAvailableCameras;
            }
        }
    }
}
